package com.pp.common.util;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;

/**
 * JWT 工具类
 */
public class JwtUtils {

    private static final long EXPIRATION_TIME = 3600_000_000L; // 1000 hours
    private static final String SECRET = "ThisIsASecret"; // Self-defined secret
    private static final String TOKEN_PREFIX = "Bearer "; // JWT prefix
    private static final String HEADER_STRING = "Authorization"; // JWT HTTP request header name
    public static final String USERNAME = "username"; // HTTP request header name


    /**
     * 基于负载内容和算法创建token信息。
     */
    public static String generateToken(String username) {
        HashMap<String, Object> map = new HashMap<>();
        // You can put any data in the map
        map.put(USERNAME, username);
        return Jwts.builder()
                .setClaims(map)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact(); // JWT 前面一般都会加 "Bearer " 前缀
    }

    /**
     * 校验从前端传过来的 token
     */
    public static HttpServletRequest validateTokenAndAddUserIdToHeader(HttpServletRequest request) throws TokenValidationException {
        // 获取 request 对象中的验证请求头 Authorization 对应的 JWT 信息
        String token = request.getHeader(HEADER_STRING);

        // 若有 Authorization 头信息则开始解析 JWT，得到其中当前用户的 username
        if (token != null) {
            try {
                // body: { "username": "flora", "exp": 1633839905 }
                Map<String, Object> body = Jwts.parser()
                        .setSigningKey(SECRET)
                        // 去掉 "Bearer " 前缀
                        .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                        .getBody();

                // 把从 JWT 中解析得到的 username 信息追加到原有的 request 对象的请求头中
                return new CustomHttpServletRequest(request, body);
            } catch (Exception e) {
                throw new TokenValidationException(e.getMessage());
            }
        } else {
            // 若没有 Authorization 头信息则抛出自定义的 TokenValidationException 异常给当前方法的调用者
            throw new TokenValidationException("Missing token");
        }
    }

    /**
     * 自定义的 HttpServletRequest 类
     * 这种自定义的方式使用了装饰器模式 (Decorator Pattern):
     * 允许向一个现有的对象添加新的功能，同时又不改变其结构
     */
    public static class CustomHttpServletRequest extends HttpServletRequestWrapper {

        private final Map<String, String> claims;

        public CustomHttpServletRequest(HttpServletRequest request, Map<String, ?> claims) {
            super(request);
            this.claims = new HashMap<>();
            // Populate member 'claims'
            claims.forEach((k, v) -> this.claims.put(k, String.valueOf(v)));
        }

        // 自定义的 CustomHttpServletRequest 必须提供一个 getHeaders 方法
        @Override
        public Enumeration<String> getHeaders(String name) {
            // 若包含相应的 key，则返回对应的值 value
            // Validate member 'claims'
            if (claims != null && claims.containsKey(name))
                return Collections.enumeration(
                    Collections.singletonList(claims.get(name))
                );

            return super.getHeaders(name);
        }
    }

    /**
     * 自定义 TokenValidationException 异常类
     */
    static class TokenValidationException extends RuntimeException {
        public TokenValidationException(String message) {
            super(message);
        }
    }

}